package com.everflourish.yeah100.utils.markingsystem;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 解析答题卡51*32的工具类
 *
 * @author datian
 */
public class Parse51x32Util {
    // 学生的id，即学号 键
    public static final String STUDENT_ID = "student_id";
    // 科目的键
    public static final String COURSE_ID = "course_id";
    // 科目
    private static final String[][] COURSE = {{"1", "政治"}, {"2", "语文"},
            {"3", "数学"}, {"4", "物理"}, {"5", "化学"}, {"6", "外语"},
            {"7", "历史"}, {"8", "地理"}, {"9", "生物"}, {"10", "文综"},
            {"11", "理综"}};

    // 接受的总字节数
    private static final int TOTAL_BYTE_COUNT = 204;
    // 行的数量
    private static final int TOTAL_ROW_COUNT = 51;
    // 列的数量
    private static final int TOTAL_RANK_COUNT = 32;
    // 模型
    private static final int PATTERNS[][] = {{1, 1}, {2, 2}, {3, 3},
            {4, 4}, {5, 8}, {6, 12}, {7, 16}, {8, 20}, {9, 24},
            {10, 28}, {11, 5}, {12, 9}, {13, 13}, {14, 17},
            {15, 21}, {16, 25}, {17, 29}, {18, 6}, {19, 10},
            {20, 14}, {21, 18}, {22, 22}, {23, 26}, {24, 30},
            {25, 7}, {26, 11}, {27, 15}, {28, 19}, {29, 23},
            {30, 27}, {31, 31}, {32, 32}};

    /**
     * 1..... 将byte数组中的数转为二进制
     *
     * @param byteList 存放在list中的字节集合
     * @return 转换后的二进制数集合
     */
    public static List<Integer> byteToBinary(List<Byte> byteList) {
        List<Integer> binaryList = null;
        try {
            binaryList = new ArrayList<Integer>();
            int[] arrUnsignedInt = new int[TOTAL_BYTE_COUNT]; // 存放的是无符号的整形204
            // TODO，到时候i要改为0，因为开发和测试阶段，默认多发送了10个字节
            for (int i = 10, j = 0; i < TOTAL_BYTE_COUNT + 10; i++) { // 发送过来的是204个字节
                arrUnsignedInt[j++] = byteList.get(i) & 0xFF;// 转成无符号的整形
            }
            // --------------------------------将arrUnsignedInt中的无符号转为二进制--------------------------------------
            int[] arrData = new int[TOTAL_ROW_COUNT]; // 一共有51行
            int tempK = 0, nCur = 0, nVal, nData;
            for (int i = 0; i < TOTAL_BYTE_COUNT; i++) {
                int tempUnsignedInt = arrUnsignedInt[i];
                nCur |= tempUnsignedInt << ((3 - (i % 4)) * 8);
                if (0 == (i + 1) % 4) {
                    arrData[tempK++] = nCur;
                    nCur = 0;
                }
            }

            for (int i = 0; i < TOTAL_ROW_COUNT; i++) {// 51行
                nVal = arrData[i];
                for (int j = 0; j < TOTAL_RANK_COUNT; j++) {// 32列
                    nData = nVal & (1 << (31 - j)); // 取出其中的一位
                    binaryList.add((nData > 0) ? 1 : 0);
                }
            }
            // -----------------------------------------------------------------------------------------------
        } catch (Exception e) {
            e.printStackTrace();
        }
        return binaryList;
    }

    /**
     * 2.... 将二进制的集合转换为map集合 map集合存放的形式是，学号和科目代号:string-string,答案：string-list
     *
     * @param binaryList 二进制集合
     * @return map集合（已经解析好的）
     */
    public static Map<String, Object> binaryToAnswer(List<Integer> binaryList) {
        if (binaryList == null
                || binaryList.size() != TOTAL_RANK_COUNT * TOTAL_ROW_COUNT) {
            return null;
        }

        int binaryTypeset[][] = new int[TOTAL_ROW_COUNT][TOTAL_RANK_COUNT];
        for (int i = 0, k = 0; i < binaryList.size(); i += TOTAL_RANK_COUNT, k++) {
            List<Integer> subList = binaryList.subList(i, i + TOTAL_RANK_COUNT); // 截取一行的数据，一行为32位。

            for (int j = 0; j < subList.size(); j++) {// 遍历行，根据模板纠正位置
                int pos = PATTERNS[j][1] - 1; // 排版是从1开始算起的，但是数组是从0开始的，所以减1。
                binaryTypeset[k][j] = subList.get(pos);// 存放到这里后，是根据模板纠正后的一个二维数组，51行32列
            }
        }

        return parseBinaryTypeset(binaryTypeset);
    }

    /**
     * 3..... 将排版好的二进制二维数组进行解析为答案
     *
     * @param binaryTypeset 排版好的二进制二维数组
     * @return map集合，即答案：如：1-B，2-C
     */
    public static Map<String, Object> parseBinaryTypeset(int[][] binaryTypeset) {
        Map<String, Object> map = new HashMap<String, Object>();
        String sNumber = "";
        // 判断学号是那个数字？【0--9】
        int numberFlag = 0;
        // 一列中的涂的号码个数，（ 学号一列中，只能涂一个地方）。
        int numberCount = 0;
        // 判断学号
        for (int j = 13; j < 22; j++) {// 行，从14列到22列，下标从0开始，所以13即对应第14行
            for (int i = 1, k = 0; i < 11; i++, k++) { // 行，从第2行到11行。下标从0开始，所以1即对应第2列
                int isOne = binaryTypeset[i][j]; // 二维数组，第一个代表列，第二个代表行
                if (isOne == 1) {
                    numberFlag = k;
                    numberCount++;
                }
            }
            if (numberCount == 1) {// 判断涂了多个数字，如果不是一个，直接报错
                sNumber += numberFlag;
                numberFlag = 0;
            } else {
                sNumber = "读取学号失败!";
                break;
            }
            numberCount = 0;
        }
        map.put(STUDENT_ID, sNumber);

        List<String> singleAnswerList = new ArrayList<String>();

        // 判断学科
        for (int i = 1; i < 12; i++) { // 列，从第2行到12行。下标从0开始，所以1即对应第2列
            int isOne = binaryTypeset[i][25]; // 二维数组，第一个代表列，第二个代表行 25表示第26列
            if (isOne == 1) {
                String tempCourse = COURSE[i - 1][1];
                singleAnswerList.add(tempCourse);
            }
        }
        map.put(COURSE_ID, singleAnswerList);
        singleAnswerList = new ArrayList<String>();

        String tempFlag = "";
        int tCount = 0;
        // 判断答案(1到20题)
        for (int j = 3; j < 26; j++) { // 列，从第4列(下标对应为3)开始，到第26列（下标对应为25）
            if (j == 8 || j == 14 || j == 20) {// 间隔空白区域
                continue;
            }
            for (int i = 15, k = 0; i < 19; i++, k++) {// 行，从第16行（下标对应为15）开始，到第39行，下标对应为28
                // ，k只是一个标记，0对应A,1对应B，2对应C,3对应D
                int isOne = binaryTypeset[i][j];
                if (isOne == 1) {// 为1表示黑色部分
                    switch (k) {
                        case 0:
                            tempFlag = "A";
                            break;
                        case 1:
                            tempFlag = "B";
                            break;
                        case 2:
                            tempFlag = "C";
                            break;
                        case 3:
                            tempFlag = "D";
                            break;
                    }
                    singleAnswerList.add(tempFlag); // 将一道题的答案存放到一个list集合中，因为题型包含多项选择题
                    tempFlag = "";
                }
            }
            map.put(++tCount + "", singleAnswerList);
            singleAnswerList = new ArrayList<String>();
        }
        // 判断答案(21到40题)
        for (int j = 3; j < 26; j++) { // 列，从第4列(下标对应为3)开始，到第26列（下标对应为25）
            if (j == 8 || j == 14 || j == 20) {// 间隔空白区域
                continue;
            }
            for (int i = 21, k = 0; i < 25; i++, k++) {// 行，从第16行（下标对应为15）开始，到第39行，下标对应为28
                // ，k只是一个标记，0对应A,1对应B，2对应C,3对应D
                int isOne = binaryTypeset[i][j];
                if (isOne == 1) {// 为1表示黑色部分
                    switch (k) {
                        case 0:
                            tempFlag = "A";
                            break;
                        case 1:
                            tempFlag = "B";
                            break;
                        case 2:
                            tempFlag = "C";
                            break;
                        case 3:
                            tempFlag = "D";
                            break;
                    }
                    singleAnswerList.add(tempFlag); // 将一道题的答案存放到一个list集合中，因为题型包含多项选择题
                    tempFlag = "";
                }
            }
            map.put(++tCount + "", singleAnswerList);
            singleAnswerList = new ArrayList<String>();
        }
        // 判断答案(41到60题)
        for (int j = 3; j < 26; j++) { // 列，从第4列(下标对应为3)开始，到第26列（下标对应为25）
            if (j == 8 || j == 14 || j == 20) {// 间隔空白区域
                continue;
            }
            for (int i = 27, k = 0; i < 31; i++, k++) {// 行，从第16行（下标对应为15）开始，到第39行，下标对应为28
                // ，k只是一个标记，0对应A,1对应B，2对应C,3对应D
                int isOne = binaryTypeset[i][j];
                if (isOne == 1) {// 为1表示黑色部分
                    switch (k) {
                        case 0:
                            tempFlag = "A";
                            break;
                        case 1:
                            tempFlag = "B";
                            break;
                        case 2:
                            tempFlag = "C";
                            break;
                        case 3:
                            tempFlag = "D";
                            break;
                    }
                    singleAnswerList.add(tempFlag); // 将一道题的答案存放到一个list集合中，因为题型包含多项选择题
                    tempFlag = "";
                }
            }
            map.put(++tCount + "", singleAnswerList);
            singleAnswerList = new ArrayList<String>();
        }
        // 判断答案(61到75题)
        for (int j = 3; j < 20; j++) { // 列，从第4列(下标对应为3)开始，到第26列（下标对应为25）
            if (j == 8 || j == 14 || j == 20) {// 间隔空白区域
                continue;
            }
            for (int i = 33, k = 0; i < 37; i++, k++) {// 行，从第16行（下标对应为15）开始，到第39行，下标对应为28
                // ，k只是一个标记，0对应A,1对应B，2对应C,3对应D
                int isOne = binaryTypeset[i][j];
                if (isOne == 1) {// 为1表示黑色部分
                    switch (k) {
                        case 0:
                            tempFlag = "A";
                            break;
                        case 1:
                            tempFlag = "B";
                            break;
                        case 2:
                            tempFlag = "C";
                            break;
                        case 3:
                            tempFlag = "D";
                            break;
                    }
                    singleAnswerList.add(tempFlag); // 将一道题的答案存放到一个list集合中，因为题型包含多项选择题
                    tempFlag = "";
                }
            }
            map.put(++tCount + "", singleAnswerList);
            singleAnswerList = new ArrayList<String>();
        }
        // 判断答案(76到90题)
        for (int j = 3; j < 20; j++) { // 列，从第4列(下标对应为3)开始，到第26列（下标对应为25）
            if (j == 8 || j == 14 || j == 20) {// 间隔空白区域
                continue;
            }
            for (int i = 39, k = 0; i < 43; i++, k++) {// 行，从第16行（下标对应为15）开始，到第39行，下标对应为28
                // ，k只是一个标记，0对应A,1对应B，2对应C,3对应D
                int isOne = binaryTypeset[i][j];
                if (isOne == 1) {// 为1表示黑色部分
                    switch (k) {
                        case 0:
                            tempFlag = "A";
                            break;
                        case 1:
                            tempFlag = "B";
                            break;
                        case 2:
                            tempFlag = "C";
                            break;
                        case 3:
                            tempFlag = "D";
                            break;
                    }
                    singleAnswerList.add(tempFlag); // 将一道题的答案存放到一个list集合中，因为题型包含多项选择题
                    tempFlag = "";
                }
            }
            map.put(++tCount + "", singleAnswerList);
            singleAnswerList = new ArrayList<String>();
        }
        // 判断答案(91到105题)
        for (int j = 3; j < 20; j++) { // 列，从第4列(下标对应为3)开始，到第26列（下标对应为25）
            if (j == 8 || j == 14 || j == 20) {// 间隔空白区域
                continue;
            }
            for (int i = 45, k = 0; i < 49; i++, k++) {// 行，从第16行（下标对应为15）开始，到第39行，下标对应为28
                // ，k只是一个标记，0对应A,1对应B，2对应C,3对应D
                int isOne = binaryTypeset[i][j];
                if (isOne == 1) {// 为1表示黑色部分
                    switch (k) {
                        case 0:
                            tempFlag = "A";
                            break;
                        case 1:
                            tempFlag = "B";
                            break;
                        case 2:
                            tempFlag = "C";
                            break;
                        case 3:
                            tempFlag = "D";
                            break;
                    }
                    singleAnswerList.add(tempFlag); // 将一道题的答案存放到一个list集合中，因为题型包含多项选择题
                    tempFlag = "";
                }
            }
            map.put(++tCount + "", singleAnswerList);
            singleAnswerList = new ArrayList<String>();
        }
        return map;
    }

}
